﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using GolfLeague.EditModels;
using GolfLeague.ViewModels;

namespace GolfLeague.Models
{
    public class RoundInterface
    {
        GolfLeagueEntities golfDB = new GolfLeagueEntities();

        public IQueryable<Round> GetAllRounds()
        {
            return from dateEntity in golfDB.Rounds
                   orderby dateEntity.RoundDate
                   select dateEntity;
        }

        public IQueryable<Round> GetAllScheduleRounds(int scheduleID)
        {
            return from dateEntity in golfDB.Rounds
                   where dateEntity.ScheduleID.Equals(scheduleID)
                   orderby dateEntity.RoundDate
                   select dateEntity;
        }

        public Round GetRound(int roundID)
        {
            var dateQuery = from dateEntity in golfDB.Rounds
                            where dateEntity.RoundID.Equals(roundID)
                            select dateEntity;

            return dateQuery.FirstOrDefault();
        }

        public IQueryable<RoundScoringMethod> GetRoundScoringMethods(int roundID)
        {
            return from dateEntity in golfDB.RoundScoringMethods
                   where dateEntity.RoundID.Equals(roundID)
                   select dateEntity;
        }

        public IQueryable<Side> GetSides()
        {
            return from sideEntity in golfDB.Sides
                   orderby sideEntity.SideName
                   select sideEntity;
        }

        public RoundsEditModel GetRoundEditModel(int scheduleID)
        {
            CourseInterface courseInterface = new CourseInterface();
            TeamInterface teamInterface = new TeamInterface();
            ScheduleInterface scheduleInterface = new ScheduleInterface();
            ScoreCardInterface scoreCardInterface = new ScoreCardInterface();
            ScoreInterface scoreInterface = new ScoreInterface();

            var courses = courseInterface.GetAllCourses();
            int courseID = courses.FirstOrDefault().CourseID;
            IEnumerable<Round> rounds = GetAllScheduleRounds(scheduleID);

            if (rounds.Count() > 0)
            {
                courseID = rounds.LastOrDefault().CourseID;
            }

            List<StandardDropdown> teamList = new List<StandardDropdown>();
            foreach (Team team in teamInterface.GetAllActiveTeams())
            {
                IEnumerable<Player> players = teamInterface.GetTeamPlayers(team.TeamID);
                teamList.Add(new StandardDropdown
                {
                    dropdownID = team.TeamID,
                    dropdownText = team.TeamName + " (" + players.First().FirstName + " " + players.First().LastName + "/" + players.Last().FirstName + " " + players.Last().LastName + ")"
                });
            }

            List<StandardDropdown> scoringMethods = new List<StandardDropdown>();
            foreach (ScoringMethod scoringMethod in scoreInterface.GetAllScoringMethods())
                scoringMethods.Add(new StandardDropdown
                {
                    dropdownID = scoringMethod.ScoringMethodID,
                    dropdownText = scoringMethod.ScoringMethodName
                });

            return new RoundsEditModel
            {
                Courses = courses,
                Rounds = rounds,
                NewRound = new Round { CourseID = courseID, ScheduleID = scheduleID },
                ScoringMethods = new List<int>(),
                Schedule = scheduleInterface.GetSchedule(scheduleID),
                ScoringMethodsDropdown = scoringMethods,
                Sides = GetSides(),
                TeeBoxes = scoreCardInterface.BuildTeeBoxDropDown(courseID),
                RoundBatchOptions = new RoundsBatchModel 
                { 
                    TeamList = teamList, 
                    NumberOfRounds = teamList.Count - 1,
                    RoundScoringMethods = new List<int>()
                }
            };
        }

        public RoundViewModel GetRoundViewModel(int roundID)
        {
            MatchInterface matchInterface = new MatchInterface();

            return new RoundViewModel
            {
                Round = GetRound(roundID),
                Matches = matchInterface.GetMatchViewModels(roundID)
            };
        }

        public IEnumerable<Match> GenerateRoundMatches(List<int> RoundIDList, List<int> TeamList, DateTime StartTime, int MatchOffSet)
        {
            List<TeamMatchList> Teams = new List<TeamMatchList>();
            List<Match> Matches;
            List<TeamMatch> TeamMatches;
            DateTime MatchStartTime;
            bool RebuildMatchList;

            TeamList.ForEach(delegate(int team) 
            {
                Teams.Add(new TeamMatchList 
                { 
                    TeamID = team
                });
            });

            do
            {
                Teams.ForEach(delegate(TeamMatchList team)
                {
                    team.TeamsPlayed = new List<int> { team.TeamID };
                });
                Matches = new List<Match>();
                TeamMatches = new List<TeamMatch>();
                RebuildMatchList = false;
                for (int roundID = 0; roundID < RoundIDList.Count; roundID ++)
                {
                    MatchStartTime = StartTime;
                    List<TeamMatchup> teamMatchList = new List<TeamMatchup>();
                    if (roundID >= TeamList.Count - 1)
                    {
                        Round nextRound = GetRound(RoundIDList[roundID]);
                        Round lastRound = GetRound(RoundIDList[roundID - TeamList.Count + 1]);
                        if (nextRound.SideID == lastRound.SideID && nextRound.SideID != (int)CourseSides.Both)
                        {
                            lastRound = GetRound(RoundIDList[roundID - TeamList.Count + 2]);
                        }

                        List<Match> lastMatches = Matches.FindAll(m => m.RoundID == lastRound.RoundID).ToList();
                        int matchCount = lastMatches.Count;
                        for (int m = 0; m < matchCount; m++)
                        {
                            Random rnd = new Random(DateTime.Now.Millisecond);
                            Match lastMatch = lastMatches.Skip(rnd.Next(0, lastMatches.Count - 1)).Take(1).Single();
                            Match newMatch = new Match
                            {
                                RoundID = RoundIDList[roundID],
                                MatchTime = MatchStartTime
                            };
                            TeamMatches.Add(new TeamMatch
                            {
                                MatchID = newMatch.MatchID
                            });

                            //Matches.Add(new Match
                            //{
                            //    RoundID = RoundIDList[roundID],
                            //    MatchTime = MatchStartTime
                            //    //TeamA = lastMatch.TeamB,
                            //    //TeamB = lastMatch.TeamA
                            //});

                            MatchStartTime = MatchStartTime.AddMinutes(MatchOffSet);
                            lastMatches.Remove(lastMatch);
                        }
                    }
                    else
                    {
                        teamMatchList = BuildMatchList(TeamList, Teams);
                        if (teamMatchList == null)
                        {
                            RebuildMatchList = true;
                            break;
                        }
                        foreach (TeamMatchup teamMatch in teamMatchList)
                        {
                            Match newMatch = new Match
                            {
                                RoundID = RoundIDList[roundID],
                                MatchTime = MatchStartTime
                            };
                            TeamMatches.Add(new TeamMatch
                            {
                                MatchID = newMatch.MatchID
                            });
                            //Matches.Add(new Match
                            //{
                            //    RoundID = RoundIDList[roundID],
                            //    MatchTime = MatchStartTime
                            //    //TeamA = teamMatch.TeamA,
                            //    //TeamB = teamMatch.TeamB
                            //});
                            MatchStartTime = MatchStartTime.AddMinutes(MatchOffSet);
                        }
                    }
                }
            } while (RebuildMatchList);

            return Matches;
        }

        public List<TeamMatchup> BuildMatchList(List<int> MasterTeamList, List<TeamMatchList> Teams)
        {
            int TeamA;
            int TeamB;
            List<TeamMatchup> MatchList = new List<TeamMatchup>();
            List<int> TeamList = MasterTeamList.ToList();
            int matchesPerRound = TeamList.Count / 2;
            bool CompleteMatchList = false;
            int BuildListTries = 0;

            while (!CompleteMatchList)
            {
                for (int matchID = 0; matchID < matchesPerRound; matchID++)
                {
                    TeamA = SelectRandomTeam(TeamList);
                    TeamB = SelectRandomTeam(TeamList.Except(Teams.Single(t => t.TeamID == TeamA).TeamsPlayed).ToList());

                    if (TeamB > 0)
                    {
                        TeamList.Remove(TeamA);
                        TeamList.Remove(TeamB);
                        Teams.Single(t => t.TeamID == TeamA).TeamsPlayed.Add(TeamB);
                        Teams.Single(t => t.TeamID == TeamB).TeamsPlayed.Add(TeamA);
                        MatchList.Add(new TeamMatchup { TeamA = TeamA, TeamB = TeamB });
                    }
                }
                if (MatchList.Count == matchesPerRound)
                {
                    CompleteMatchList = true;
                }
                else
                {
                    foreach (TeamMatchup teamMatch in MatchList)
                    {
                        Teams.Single(t => t.TeamID == teamMatch.TeamA).TeamsPlayed.Remove(teamMatch.TeamB);
                        Teams.Single(t => t.TeamID == teamMatch.TeamB).TeamsPlayed.Remove(teamMatch.TeamA);

                    }
                    TeamList = MasterTeamList.ToList();
                    MatchList.Clear();
                    BuildListTries++;
                    if (BuildListTries > 10)
                    {
                        MatchList.Clear();
                        MatchList = null;
                        break;
                    }
                }
            }

            return MatchList;
        }

        public void AddRound(Round round)
        {
            golfDB.Rounds.AddObject(round);
        }

        public void AddRoundScoringMethod(RoundScoringMethod roundScoringMethod)
        {
            golfDB.RoundScoringMethods.AddObject(roundScoringMethod);
        }

        public void DeleteRound(Round round)
        {
            golfDB.Rounds.DeleteObject(round);
        }

        public void DeleteRoundScoringMethod(RoundScoringMethod roundScoringMethod)
        {
            golfDB.RoundScoringMethods.DeleteObject(roundScoringMethod);
        }

        public void Save()
        {
            golfDB.SaveChanges();
        }

        private int SelectRandomTeam(List<int> teamList)
        {
            if (teamList.Count != 0)
            {
                Random rnd = new Random(DateTime.Now.Millisecond);
                return teamList.Skip(rnd.Next(0, teamList.Count())).Take(1).Single();
            }
            else
            {
                return -1;
            }
        }

    }

    public class TeamMatchList
    {
        public int TeamID { get; set; }
        public List<int> TeamsPlayed { get; set; }
        public List<int> TeamsPlayedFront { get; set; }
        public List<int> TeamsPlayedBack { get; set; }
        public TeamMatchList()
        {
            TeamsPlayed = new List<int>();
            TeamsPlayedFront = new List<int>();
            TeamsPlayedBack = new List<int>();
        }
    }

    public class TeamMatchup
    {
        public int TeamA { get; set; }
        public int TeamB { get; set; }
    }

    public enum CourseSides
    {
        Front = 1,
        Back = 2,
        Both = 3
    }
}